/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.src.nodes;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Arrays;
import java.util.List;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.cookies.FilterCookie;
import org.openide.src.ElementProperties;
import org.openide.src.SourceElement;
import org.openide.src.ClassElement;
import org.openide.src.Identifier;
import org.openide.util.WeakListener;
/** Normal implementation of children for source element nodes.
* <P>
* Ordering and filtering of the children can be customized
* using {@link SourceElementFilter}.
* {@link FilterCookie} is implemented to provide a means
* for user customization of the filter.
* <p>The child list listens to changes in the source element, as well as the filter, and
* automatically updates itself as appropriate.
* <p>A child factory can be used to cause the children list to create
* non-{@link DefaultFactory default} child nodes, if desired, both at the time of the creation
* of the children list, and when new children are added.
* <p>The children list may be unattached to any source element temporarily,
* in which case it will have no children (except possibly an error indicator).
*
* @author Dafe Simonek, Jan Jancura
*/
public class SourceChildren extends Children.Keys implements FilterCookie {
/** The key describing state of source element */
static final Object NOT_KEY = new Object();
/** The key describing state of source element */
static final Object ERROR_KEY = new Object();
/** PACKAGE modifier support */
private static int PPP_MASK = SourceElementFilter.PUBLIC +
SourceElementFilter.PRIVATE +
SourceElementFilter.PROTECTED;
/** The element whose subelements are represented. */
protected SourceElement element;
/** Filter for elements. Can be <code>null</code>, in which case
* modifier filtering is disabled, and ordering may be reset to the default order. */
protected SourceElementFilter filter;
/** Factory for obtaining class nodes. */
protected ElementNodeFactory factory;
/** Weak listener to the element and filter changes */
private PropertyChangeListener wPropL;
/** Listener to the element and filter changes. This reference must
* be kept to prevent the listener from finalizing when we are alive */
private ElementListener propL;
/** Flag saying whether we have our nodes initialized */
private boolean nodesInited = false;
// init ................................................................................
/** Create a children list with the default factory and no attached source element.
*/
public SourceChildren () {
this (DefaultFactory.READ_WRITE, null);
}
/** Create a children list with the default factory.
* @param element source element to attach to, or <code>null</code>
*/
public SourceChildren (final SourceElement element) {
this(DefaultFactory.READ_WRITE, element);
}
/** Create a children list with no attached source element.
* @param factory a factory for creating children
*/
public SourceChildren (final ElementNodeFactory factory) {
this(factory, null);
}
/** Create a children list.
* @param factory a factory for creating children
* @param element source element to attach to, or <code>null</code>
*/
public SourceChildren (final ElementNodeFactory factory,
final SourceElement element) {
super();
this.element = element;
this.factory = factory;
this.filter = new SourceElementFilter ();
}
// FilterCookie implementation .............................................................
/* @return The class of currently asociated filter or null
* if no filter is asociated with these children.
*/
public Class getFilterClass () {
return SourceElementFilter.class;
}
/* @return The filter currently asociated with these children
*/
public Object getFilter () {
return filter;
}
/* Sets new filter for these children.
* @param filter New filter. Null == disable filtering.
*/
public void setFilter (final Object filter) {
if (!(filter instanceof SourceElementFilter))
throw new IllegalArgumentException();
this.filter = (SourceElementFilter)filter;
// change element nodes according to the new filter
if (nodesInited)
refreshKeys ();
}
// Children implementation ..............................................................
/* Overrides initNodes to run the preparation task of the
* source element, call refreshKeys and start to
* listen to the changes in the element too. */
protected void addNotify () {
if (element != null) {
// listen to the source element property changes
if (wPropL == null) {
propL = new ElementListener();
wPropL = WeakListener.propertyChange(propL, element);
}
element.addPropertyChangeListener (wPropL);
element.prepare();
}
refreshKeys ();
nodesInited = true;
}
protected void removeNotify () {
setKeys (java.util.Collections.EMPTY_SET);
nodesInited = false;
}
/* Create nodes for given key.
* The node is created using node factory.
*/
protected Node[] createNodes (final Object key) {
// find out the type of the key and create appropriate node
if (key instanceof ClassElement)
return new Node[] { factory.createClassNode((ClassElement)key) };
if (NOT_KEY.equals(key))
return new Node[] { factory.createWaitNode() };
// never should get here
return new Node[] { factory.createErrorNode() };
}
// main public methods ..................................................................
/** Get the currently attached source element.
* @return the element, or <code>null</code> if unattached
*/
public SourceElement getElement () {
return element;
}
/** Set a new source element to get information about children from.
* @param element the new element, or <code>null</code> to detach
*/
public void setElement (final SourceElement element) {
if (this.element != null) {
this.element.removePropertyChangeListener(wPropL);
}
this.element = element;
if (this.element != null) {
if (wPropL == null) {
propL = new ElementListener();
wPropL = WeakListener.propertyChange(propL, this.element);
}
this.element.addPropertyChangeListener(wPropL);
}
// change element nodes according to the new element
if (nodesInited) {
if (this.element != null) this.element.prepare();
refreshKeys ();
}
}
// other methods ..........................................................................
/** Refreshes the keys according to the current state of the element and
* filter etc.
* (This method is also called when the change of properties occurs either
* in the filter or in the element)
* PENDING - (for Hanz - should be implemented better, change only the
* keys which belong to the changed property).
* @param evt the event describing changed property (or null to signalize
* that all keys should be refreshed)
*/
private void refreshKeys () {
int status = (element == null) ? SourceElement.STATUS_ERROR
: element.getStatus();
switch (status) {
case SourceElement.STATUS_NOT:
setKeys(new Object[] { NOT_KEY });
// start parsing
element.prepare ();
break;
case SourceElement.STATUS_ERROR:
setKeys(new Object[] { ERROR_KEY });
break;
case SourceElement.STATUS_PARTIAL:
case SourceElement.STATUS_OK:
refreshAllKeys();
break;
}
}
/** Updates all the keys (elements) according to the current
* filter and ordering */
private void refreshAllKeys () {
int[] order = (filter == null || (filter.getOrder() == null))
? SourceElementFilter.DEFAULT_ORDER : filter.getOrder();
LinkedList keys = new LinkedList();
// build ordered and filtered keys for the subelements
for (int i = 0; i < order.length; i++)
addKeysOfType(keys, order[i]);
// set new keys
setKeys(keys);
}
/** Filters and adds the keys of specified type to the given
* key collection.
*/
private void addKeysOfType (Collection keys, final int elementType) {
if (elementType == SourceElementFilter.IMPORT) {
// PENDING imports are not solved yet...maybe ImportsChildren???
//keys.addAll(Arrays.asList(element.getImports()));
return;
} else {
List cls;
if ((filter != null) && filter.isAllClasses()) {
cls = Arrays.asList (element.getAllClasses ());
} else {
cls = Arrays.asList (element.getClasses ());
}
int i = cls.size () - 1;
for (; i >= 0 ; i--) {
ClassElement classElement = (ClassElement)cls.get (i);
int modifiers = classElement.getModifiers ();
if ((modifiers & PPP_MASK) == 0) modifiers += SourceElementFilter.PACKAGE;
if ((filter.getModifiers () & modifiers) == 0) continue;
if (classElement.isClass ()) {
if ((elementType & SourceElementFilter.CLASS) != 0) keys.add (classElement);
} else
if ((elementType & SourceElementFilter.INTERFACE) != 0) keys.add (classElement);
}
}
}
// innerclasses ...........................................................................
/** The listener for listening to the property changes in the filter.
*/
private final class ElementListener implements PropertyChangeListener {
public void propertyChange (PropertyChangeEvent evt) {
boolean refresh = ElementProperties.PROP_CLASSES.equals(evt.getPropertyName());
if (!refresh && ElementProperties.PROP_STATUS.equals(evt.getPropertyName())) {
Integer val = (Integer) evt.getNewValue();
refresh = ((val == null) || (val.intValue() != SourceElement.STATUS_NOT));
}
if (refresh)
refreshKeys();
}
} // end of ElementListener inner class
}
/*
* Log
* 24 src-jtulach1.23 11/5/99 Jaroslav Tulach WeakListener has now
* registration methods.
* 23 src-jtulach1.22 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 22 src-jtulach1.21 8/27/99 Petr Hamernik optimization of
* ElementListener events handling
* 21 src-jtulach1.20 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 20 src-jtulach1.19 5/15/99 Jaroslav Tulach
* 19 src-jtulach1.18 5/15/99 Jaroslav Tulach Changes in hierarchy to
* work better in DataObjectFilter
* 18 src-jtulach1.17 5/13/99 Jan Jancura System.out.println
* cleared
* 17 src-jtulach1.16 5/13/99 Jan Jancura Bug in lazy
* initialization
* 16 src-jtulach1.15 4/22/99 Jaroslav Tulach When status NOT_PARSED
* starts parsing.
* 15 src-jtulach1.14 4/21/99 Jan Jancura Rolled back - bug in
* parsing
* 14 src-jtulach1.13 4/21/99 Jan Jancura Optimalization in
* Children.Keys applied
* 13 src-jtulach1.12 4/16/99 Jaroslav Tulach Changes in children.
* 12 src-jtulach1.11 4/13/99 Petr Hamernik bugfix - children stays
* in "Please wait..."
* 11 src-jtulach1.10 4/2/99 Jesse Glick [JavaDoc]
* 10 src-jtulach1.9 4/2/99 Jan Jancura ObjectBrowser Support
* 9 src-jtulach1.8 3/26/99 Petr Hamernik small improvements
* 8 src-jtulach1.7 3/16/99 Petr Hamernik renaming static fields
* 7 src-jtulach1.6 3/15/99 Petr Hamernik
* 6 src-jtulach1.5 2/11/99 David Simonek
* 5 src-jtulach1.4 2/10/99 David Simonek
* 4 src-jtulach1.3 2/9/99 David Simonek little fixes - init in
* separate thread
* 3 src-jtulach1.2 2/3/99 David Simonek getting it to work
* properly
* 2 src-jtulach1.1 2/1/99 David Simonek
* 1 src-jtulach1.0 1/29/99 David Simonek
* $
*/